package com.hly.poitool.word;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.main.STPositiveCoordinate;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTEffectExtent;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;


import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author : Jack Hao
 * @Date : 2022/3/29 10:49
 * @description :
 */

public class PoiConvertWord {


    /**
     * 把源文件中的的段落 表格 图片 合并到 目标文件中
     *
     * @param sourceFilePath 源文件路径
     * @param targetFilePath 目标文件路径
     * @param matchKey       匹配用的字符
     * @return
     */
    public static void convert(String sourceFilePath, String targetFilePath, String matchKey) throws Exception {
        ReentrantLock lock = new ReentrantLock();
        XWPFDocument document = null;
        try {
            lock.lock();

            //源文件的 XWPFDocument 对象
            document = getDocument(sourceFilePath);

            //源文件中的所有数据
            List<IBodyElement> bodyElements = document.getBodyElements();

            //源文件中的段落
            List<XWPFParagraph> paraList = document.getParagraphs();

            //源文件中表格
            List<XWPFTable> tables = document.getTables();

            //源文件中的图片
            List<XWPFPictureData> pictures = document.getAllPictures();

            // 把原文将中的所有数据添加到目标文件中
            setRed(bodyElements, paraList, tables, pictures, targetFilePath, matchKey);

        } finally {
            try {
                lock.unlock();
            } catch (Exception e) {
                e.printStackTrace();
            }

            if(document!=null){
                try {
                    document.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 获取 body Element 中的 段落 表格 和 图片
     *
     * @param bodyElements   源文件 的 body 节点
     * @param paraList       源文件 的段落集合
     * @param tables         源文件 的表格集合
     * @param pictures       源文件 的图片集合
     * @param targetFilePath 目标文件的路径
     * @param matchKey       匹配用的标识符
     * @return
     * @throws Exception
     */
    public static boolean setRed(List<IBodyElement> bodyElements, List<XWPFParagraph> paraList, List<XWPFTable> tables, List<XWPFPictureData> pictures, String targetFilePath, String matchKey) throws Exception {
        XWPFDocument targetDocument = null;//构建模板文档对象
        FileOutputStream fileOutputStream = null;
        try {
            //使用 byte 数组 新建 XWPFDocument 对象
            targetDocument = getDocument(targetFilePath);

            //获取目标文件的所有段落对象
            List<XWPFParagraph> targetParas = targetDocument.getParagraphs();

            //匹配到的段落的位置
            int startPoint = getPoint(targetParas, matchKey);

            if (startPoint != 0) {

                XWPFParagraph insertNewParagraph = targetParas.get(startPoint);//获取到标记位置的段落对象
                XmlCursor xmlCursor = insertNewParagraph.getCTP().newCursor();//获取到段落的光标

                xmlCursor.toNextSibling();//下移光标
                insertNewParagraph = targetDocument.insertNewParagraph(xmlCursor);//在当前光标处插入新的段落
                xmlCursor = insertNewParagraph.getCTP().newCursor();

                int paraIndex = 1;
                int tableIndex = 0;
                int pictureIndex = 0;

                int beSize = bodyElements.size();
                for (int j = 1; j < beSize; j++) {


                    IBodyElement iBodyElement = bodyElements.get(j);//获得传过来的段落对象
                    BodyElementType elementType = iBodyElement.getElementType();

                    if (elementType.equals(BodyElementType.PARAGRAPH)) {//判断 文件 body 中当前节点 是段落

                        //源文件中 第 paraIndex 个 段落
                        XWPFParagraph xwpfParagraph_i = paraList.get(paraIndex);
                        paraIndex++;

                        //复制段落
                        copyParagraph(xwpfParagraph_i, insertNewParagraph, pictures, pictureIndex);

                    } else if (elementType.equals(BodyElementType.TABLE)) {//判断 文件 body 中当前节点 是表格

                        //源 文件中 获取 第 tableIndex个 table
                        XWPFTable xwpfTable = tables.get(tableIndex);
                        tableIndex++;
                        //复制table
                        xmlCursor = copyTable(targetDocument, xmlCursor, xwpfTable);
                    }

                    if(j<(beSize-1)){
                        //xmlCursor = insertNewParagraph.getCTP().newCursor();
                        xmlCursor.toNextSibling();//下移光标
                        insertNewParagraph = targetDocument.insertNewParagraph(xmlCursor);//在当前光标处插入新的段落
                        xmlCursor = insertNewParagraph.getCTP().newCursor();
                    }

                }
            }

            fileOutputStream = new FileOutputStream(targetFilePath);
            targetDocument.write(fileOutputStream);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (targetDocument != null) {
                try {
                    targetDocument.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return true;
    }


    /**
     * 复制 源文件中的段落到目标文件 段落有两种 一种是文字 另一种是图片 需要进行判断
     *
     * @param sourceParagraph 源文件的段落
     * @param targetParagraph 目标文件的段落
     * @param pictures        图片集合
     * @param pictureIndex    获取到图标的下角标
     * @return
     * @throws IOException
     * @throws InvalidFormatException
     */
    public static void copyParagraph(XWPFParagraph sourceParagraph, XWPFParagraph targetParagraph, List<XWPFPictureData> pictures, int pictureIndex) throws IOException, InvalidFormatException {
        List<XWPFRun> runs = sourceParagraph.getRuns();//获取run对象

        //复制源段落样式到新的段落
        ParagraphAlignment alignment = sourceParagraph.getAlignment();
        if (alignment != null) {
            targetParagraph.setAlignment(alignment);
        }

        for (int k = 0; k < runs.size(); k++) {

            XWPFRun oldRun = runs.get(k);
            CTR oldCtr = oldRun.getCTR();
            List<CTDrawing> drawingList = oldCtr.getDrawingList();
            CTRPr oldRPr = oldCtr.getRPr();
            if (oldRPr != null) {
                CTRPrChange oldRPrChange = oldRPr.getRPrChange();//run标签里也有rprchange标签也需要像上面一样设置
                if (oldRPrChange != null) {
                    oldRPr.unsetRPrChange();
                }
            }
            XWPFRun newRun = targetParagraph.createRun();
            if (oldRPr != null) {
                newRun.getCTR().setRPr(oldRPr);
            }

            if (drawingList.size() > 0) { //判断是图片 复制图片

                CTDrawing drawing = drawingList.get(0);
                List<CTInline> inlineList = drawing.getInlineList();
                CTInline ctInline = inlineList.get(0);
                CTPositiveSize2D extent = ctInline.getExtent();
                long cx = extent.getCx();
                long cy = extent.getCy();

                //图片在word中的宽
                int w = new Long(cx).intValue();
                //图片在word中的高
                int y = new Long(cy).intValue();

                XWPFPictureData xwpfPictureData = pictures.get(pictureIndex);
                pictureIndex++;
                byte[] data = xwpfPictureData.getData();

                String picName = "drw-"+ UUID.randomUUID().toString();
                newRun.addPicture(new ByteArrayInputStream(data), XWPFDocument.PICTURE_TYPE_PNG, picName, w, y);

            } else {//不是图片 复制文字
                String text = oldRun.text();
                newRun.setText(text);
            }
            targetParagraph.addRun(newRun);
        }
    }

    /**
     * 复制 源文件中的段落到目标文件 段落有两种 一种是文字 另一种是图片 需要进行判断
     *
     * @param sourceParagraph 源文件的段落
     * @param targetParagraph 目标文件的段落
     * @return
     * @throws IOException
     * @throws InvalidFormatException
     */
    public static void copyParagraph(XWPFParagraph sourceParagraph, XWPFParagraph targetParagraph) {
        List<XWPFRun> runs = sourceParagraph.getRuns();//获取run对象

        //复制源段落样式到新的段落
        ParagraphAlignment alignment = sourceParagraph.getAlignment();
        if (alignment != null) {
            targetParagraph.setAlignment(alignment);
        }

        for (int k = 0; k < runs.size(); k++) {

            XWPFRun oldRun = runs.get(k);
            CTR oldCtr = oldRun.getCTR();
            List<CTDrawing> drawingList = oldCtr.getDrawingList();
            CTRPr oldRPr = oldCtr.getRPr();
            if (oldRPr != null) {
                CTRPrChange oldRPrChange = oldRPr.getRPrChange();//run标签里也有rprchange标签也需要像上面一样设置
                if (oldRPrChange != null) {
                    oldRPr.unsetRPrChange();
                }
            }
            XWPFRun newRun = targetParagraph.createRun();
            if (oldRPr != null) {
                newRun.getCTR().setRPr(oldRPr);
            }

            if (drawingList.size() > 0) { //判断是图片 复制图片
                //不处理图片
            } else {//不是图片 复制文字
                String text = oldRun.text();
                newRun.setText(text);
            }
            targetParagraph.addRun(newRun);
        }
    }


    /**
     * 复制 源文件中的 table 到 目标文件
     *
     * @param targetDocument 目标文件 document 对象
     * @param xmlCursor      目标文件的光标
     * @param xwpfTable      源文件的  table
     * @return 返回光标
     */
    public static XmlCursor copyTable(XWPFDocument targetDocument, XmlCursor xmlCursor, XWPFTable xwpfTable) {

        //目标文件 中插入一个 table
        XWPFTable insertNewTable = targetDocument.insertNewTbl(xmlCursor);

        //复制源table 样式到新的table
        TableRowAlign tableAlignment = xwpfTable.getTableAlignment();
        if (tableAlignment != null) {
            insertNewTable.setTableAlignment(tableAlignment);
        }

        //获取源文件的表的行数
        List<XWPFTableRow> rows = xwpfTable.getRows();
        IBody body = xwpfTable.getBody();

        for (int i = 0; i < rows.size(); i++) {

            //源文件的 i 行
            XWPFTableRow row_i = rows.get(i);

            //第 i 行 的所有单元格
            List<XWPFTableCell> tableCells = row_i.getTableCells();

            //目标文件  table 第一行 不需要新建
            XWPFTableRow createRow_i = null;
            if (i == 0) {
                createRow_i = insertNewTable.getRow(0);
            } else {//目标文件  table 不是第一行 需要新建
                createRow_i = insertNewTable.createRow();
            }

            //空集合
            List<XWPFPictureData> pictures = new ArrayList<XWPFPictureData>();

            //便利源文件的所有单元格
            for (int k = 0; k < tableCells.size(); k++) {
                //第 k 个 单元格
                XWPFTableCell cell_k = tableCells.get(k);
                XWPFTableCell createCell_k = null;
                if (i == 0) {//第一行的单元格
                    if (k == 0) {//第一行第一个单元格 不需要新建
                        createCell_k = createRow_i.getCell(0);
                    } else {//第一行 不是第一个单元格 需要新建
                        createCell_k = createRow_i.createCell();
                    }
                } else {//不是第一行的单元格不需要新建
                    createCell_k = createRow_i.getCell(k);
                }
                if(cell_k!=null){
                    //移除单元格内默认的段落
                    createCell_k.removeParagraph(0);

                    //目标单元格 设置宽度
                    TableWidthType widthType = cell_k.getWidthType();
                    if(widthType!=null && widthType.equals(TableWidthType.DXA)){
                        createCell_k.setWidth(String.valueOf(cell_k.getWidth()));
                    }

                    List<XWPFParagraph> paragraphs = cell_k.getParagraphs();
                    for (int j = 0; j < paragraphs.size(); j++) {
                        XWPFParagraph sourceParagraph_j = paragraphs.get(j);
                        XWPFParagraph targetParagraph_j = createCell_k.addParagraph();
                        copyParagraph(sourceParagraph_j, targetParagraph_j);
                    }
                }
            }
        }
        xmlCursor = insertNewTable.getCTTbl().newCursor();
        return xmlCursor;
    }


    /**
     * 获取要匹配段落的位置
     *
     * @param paragraphs 所有的段落
     * @param matchKey   匹配的字符串
     * @return
     */
    public static int getPoint(List<XWPFParagraph> paragraphs, String matchKey) {


        if (matchKey.contains(" ")) {
            String[] split = matchKey.split(" ");
            matchKey = split[split.length - 1];
        }

        int startPoint = 0;
        //遍历模板中的段落对象找到标记位置

        //字符 匹配上
        out:
        for (int i = 0; i < paragraphs.size(); i++) {
            XWPFParagraph xwpfParagraph = paragraphs.get(i);
            List<XWPFRun> runs = xwpfParagraph.getRuns();
            if (runs != null && !runs.isEmpty()) {
                for (int l = 0; l < runs.size(); l++) {
                    XWPFRun run = runs.get(l);
                    String text = run.text();
                    if (text.contains(matchKey)) {//如果遍历到标记位置退出循环，并记录一下标记位置所在段落对象的下标
                        //xwpfParagraph.removeRun(l);//同时把匹配到的字符移除
                        startPoint = i;
                        break out;
                    }
                }
            }
        }


        return startPoint;
    }


    /**
     * 获取文件中的所有段落
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    private static List<XWPFParagraph> getParagraphs(String filePath) throws IOException {
        XWPFDocument document = getDocument(filePath);
        return getParagraphs(document);
    }


    /**
     * 获取文件中的所有段落
     *
     * @param document
     * @return
     * @throws IOException
     */
    private static List<XWPFParagraph> getParagraphs(XWPFDocument document) throws IOException {
        //源文件中的段落
        List<XWPFParagraph> paraList = document.getParagraphs();
        return paraList;
    }

    /**
     * @param filePath
     * @throws IOException
     */
    private static XWPFDocument getDocument(String filePath) throws IOException {
        //源文件流
        FileInputStream fileInputStream = new FileInputStream(new File(filePath));

        //源文件的 XWPFDocument 对象
        XWPFDocument mainDocument = new XWPFDocument(fileInputStream);

        return mainDocument;

    }

    public static void main(String[] args) {
        //合并文件
        try {
            //convert("C:\\temp\\1648695887049-7962\\3.4.2 热控分系统健康评估.docx", "C:\\temp\\1648695886735-3820\\XAST-T0002健康评估.docx", "3.4.2 热控分系统健康评估");
            //convert("C:\\temp\\\\1648725193907-6987\\4.6 测控分系统健康评估.docx", "C:\\temp\\1648725193457-5330\\XAST-T0004健康评估.docx", "4.6 测控分系统健康评估");
            convert("C:\\temp\\mxa5813640881743d9624e76ed\\4.3 综合电子分系统.docx", "C:\\temp\\mxa5813640881743d9624e76ed\\XAST-T0004正常值范围.docx", "4.3 综合电子分系统");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}


